

package com.googlecode.flac4j.format.frame.sub;


import com.googlecode.flac4j.format.frame.Frame;

import com.googlecode.jinahya.io.BitInput;
import com.googlecode.jinahya.io.BitOutput;
import com.googlecode.jinahya.util.BitAccessible;

import java.io.IOException;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;


/**
 *
 * @author Jin Kwon <jinahya at gmail.com>
 */
@XmlType(propOrder = {"subHeader", "subData"})
public class SubFrame implements BitAccessible {


    @Override
    public void read(final BitInput input) throws IOException {

        assert input.readUnsignedInt(1) == 0; // zero bit padding

        getSubHeader().read(input);

        final int subFrameType = subHeader.getType();
        if (subFrameType == 0x00) { // 000000
            subData = new CONSTANT();
        } else if (subFrameType == 0x01) { // 000001
            subData = new VERBATIM();
        } else if (subFrameType >> 3 == 0x01) { // 001xxx
            subData = new FIXED();
        } else if (subFrameType >> 5 == 0x01) { // 1xxxxx
            subData = new LPC();
        } else {
            subData = null;
        }

        if (subData != null) {
            subData.setSubFrame(this);
            subData.setSubFrameType(subFrameType);
            subData.read(input);
        }
    }


    @Override
    public void write(final BitOutput output) throws IOException {

        output.writeUnsignedInt(1, 0x00);

        subHeader.write(output);

        subData.write(output);
    }


    // ------------------------------------------------------------ parent Frame
    /**
     * Returns the parent frame of this subFrame.
     *
     * @return the parent frame
     */
    public final Frame getFrame() {
        return frame;
    }


    /**
     * Sets the parent frame of this subFrame.
     *
     * @param frame parent frame
     */
    public final void setFrame(final Frame frame) {

        if (frame == null) {
            throw new NullPointerException("null frame");
        }

        this.frame = frame;
    }


    // --------------------------------------------------------------- subHeader
    /**
     * Returns the header of this subFrame.
     *
     * @return subFrame header
     */
    public SubHeader getSubHeader() {

        if (subHeader == null) {
            subHeader = new SubHeader();
            subHeader.setSubFrame(this);
        }

        return subHeader;
    }


    // ----------------------------------------------------------------- subData
    /**
     * Returns the data of this subFrame.
     *
     * @return subFrame data
     */
    public SubData getSubData() {
        return subData;
    }


    /**
     * Sets the data of this subFrame.
     *
     * @param subData subFrame data
     */
    public void setSubData(final SubData subData) {

        if (subData == null) {
            throw new NullPointerException("null subData");
        }

        getSubHeader().setType(subData.getSubFrameType());
        subData.setSubFrame(this);
        this.subData = subData;
    }


    /** parent frame. */
    @XmlTransient
    protected Frame frame;


    /** header. */
    @XmlElement(required = true)
    private SubHeader subHeader;


    /** data. */
    @XmlElement(required = true)
    private SubData subData;


}

